#!/usr/bin/env python
#  -*- coding: utf-8 -*- 
__author__ = 'liu'

"""
KD排序策略
修改记录:
1. 2025.2.15 初次创建，策略要点：
    (1) 若干周期KDJ指标按K升降排序
    (2) 附加乖离率的计算显示
2. 2025.3.3 修改点：
    (1) 改用中文品种名
3. 2025.3.4 修改点：
    (1) 乖离率改用EMA
    (2) 修改KDJ算法
4. 2025.3.6 修改点：
    (1) 乖离率也可以选择排序
5. 2025.3.9 修改点：
    (1) 允许截取排序后前后N个品种
6. 2025.3.26 修改点：
    (1) 选升键，把带有往上箭头标识的商品取k值从小往大排序，k大于d值的
        选降键，把带有往下箭头标识的商品取k值从大往下排序 k小于d值的
7. 2025.3.27 修改点：
    (1) 处理夜间白盘显示问题
8. 2025.4.25 修改点：
    (1) 时间排列修改
    (2) 乖离率大于0，取K从小到大排行；反之，从大到小
9. 2025.5.12 修改点：
    (1) 日线和1小时图的乖离率大于0或反之的商品，k值排序
    (2) 日线和1小时图的K大于D或者反之的商品，k值排序
9. 2025.5.20 修改点：
    (1) 增加日线和1小时图的乖离率
10. 2025.5.31 修改点：
    (1) 1小时改为可以修改周期
    (2) 小时和日线栏去掉KDJ排序
"""
from datetime import datetime
from time import sleep
from sys import argv, exit
from os.path import exists
from pathlib import Path
from logging import getLogger
from account_setting import *
from common_lib_v16 import *
#from talib import STOCH
from tqsdk.ta import KDJ
from tqsdk.tafunc import ema
from tqsdk import TqApi, TqAuth, TqKq

# 记录日志并打印
def log_and_print(str):
    logger.info(str)

# 获取配置参数
def get_config_params(strategy_name, index):
    global PERIOD_A, kdj_n_A, kdj_m1_A, kdj_m2_A, bias_n_A, k_filter_A, \
           PERIOD_B, kdj_n_B, kdj_m1_B, kdj_m2_B, bias_n_B, k_filter_B
    filename = "config_" + strategy_name + ".ini"
    ser = get_config(filename, index)
    print(ser)
    if (not ser.empty):
        # 策略参数
        PERIOD_A = int(ser.iloc[0].split(':')[1])                 # 周期A（秒）
        kdj_n_A = int(ser.iloc[1])                                # 周期A KD N
        kdj_m1_A = int(ser.iloc[2])                               # 周期A KD M1
        kdj_m2_A = int(ser.iloc[3])                               # 周期A KD M2
        bias_n_A = int(ser.iloc[4])                               # 周期A BIAS N
        k_filter_A = int(ser.iloc[5])                             # 第一组过滤K
        PERIOD_B = int(ser.iloc[6])                               # 周期B（秒）
        kdj_n_B = int(ser.iloc[7])                                # 周期B KD N
        kdj_m1_B = int(ser.iloc[8])                               # 周期B KD M1
        kdj_m2_B = int(ser.iloc[9])                               # 周期B KD M2
        bias_n_B = int(ser.iloc[10])                              # 周期B BIAS N
        k_filter_B = int(ser.iloc[11])                            # 第二组过滤K

# 解析命令行入参
try:
    if len(argv) == 2:
        period_index = int(argv[1])                               # 运行周期index
    else:
        print("输入参数数量出错！")
        exit()
except Exception as InputError:
    print(InputError)
    exit()

# 策略名
CN_NAME = "KD排序"                                                # 中文名
EN_NAME = "kd_sort"                                               # 英文名

# 获取对应运行索引的配置参数
get_config_params(EN_NAME, period_index)

re_connect = True
while re_connect:
    # API实例、JSON文件名
    try:
        api = TqApi(TqKq(), auth=TqAuth(USER_SIM, PWD_SIM))
        file_name = "sim_" + USER_SIM + "_" + EN_NAME + "_" + str(period_index)
        print(api)
    except Exception as LoginError:
        print(LoginError)
        re_connect = True
        print(f"【告警】策略: {CN_NAME} 账号连接失败重连中...")
        sleep(5)
    else:
        re_connect = False

# 参数列表
kdj_n = [kdj_n_A, kdj_n_B]
kdj_m1 = [kdj_m1_A, kdj_m1_B]
kdj_m2 = [kdj_m2_A, kdj_m2_B]
bias_n = [bias_n_A, bias_n_B]
k_filter = [k_filter_A, k_filter_B]

# 周期列表
PERIOD_LIST = [PERIOD_A, PERIOD_B]

# 品种列表
SYMBOL_DICT = {
    # 上海期货交易所（SHFE）
    'KQ.m@SHFE.ag': '沪银',
    'KQ.m@SHFE.al': '沪铝',
    'KQ.m@SHFE.au': '沪金',
    'KQ.m@SHFE.bu': '沥青',
    'KQ.m@SHFE.cu': '沪铜',
    'KQ.m@SHFE.fu': '燃料油',
    'KQ.m@SHFE.hc': '热卷',
    'KQ.m@SHFE.ni': '沪镍',
    'KQ.m@SHFE.pb': '沪铅',
    'KQ.m@SHFE.rb': '螺纹钢',
    'KQ.m@SHFE.ru': '橡胶',
    'KQ.m@SHFE.sn': '沪锡',
    'KQ.m@SHFE.ss': '不锈钢',
    'KQ.m@SHFE.zn': '沪锌',
    'KQ.m@SHFE.sp': '纸浆',
    'KQ.m@SHFE.br': 'BR橡胶',
    'KQ.m@SHFE.ao': '氧化铝',

    # 上海国际能源交易中心（INE）
    'KQ.m@INE.sc': '原油',
    'KQ.m@INE.lu': '低硫油',
    'KQ.m@INE.nr': '20号胶',
    'KQ.m@INE.bc': '国际铜',
    'KQ.m@INE.ec': '集运欧线',

    # 郑州商品交易所（CZCE）
    'KQ.m@CZCE.AP': '苹果',
    'KQ.m@CZCE.CF': '棉花',
    'KQ.m@CZCE.CJ': '红枣',
    'KQ.m@CZCE.FG': '玻璃',
    'KQ.m@CZCE.MA': '甲醇',
    'KQ.m@CZCE.PF': '短纤',
    'KQ.m@CZCE.PK': '花生',
    'KQ.m@CZCE.RM': '菜籽粕',
    'KQ.m@CZCE.SA': '纯碱',
    'KQ.m@CZCE.SF': '硅铁',
    'KQ.m@CZCE.SM': '锰硅',
    'KQ.m@CZCE.SR': '白糖',
    'KQ.m@CZCE.TA': 'PTA',
    'KQ.m@CZCE.UR': '尿素',
    'KQ.m@CZCE.PR': '瓶片',
    'KQ.m@CZCE.CY': '棉纱',
    'KQ.m@CZCE.OI': '菜籽油',
    'KQ.m@CZCE.SH': '烧碱',
    'KQ.m@CZCE.PX': '对二甲苯',

    # 大连商品交易所（DCE）
    'KQ.m@DCE.a': '豆一',
    'KQ.m@DCE.b': '豆二',
    'KQ.m@DCE.c': '玉米',
    'KQ.m@DCE.cs': '淀粉',
    'KQ.m@DCE.eb': '苯乙烯',
    'KQ.m@DCE.eg': '乙二醇',
    'KQ.m@DCE.i': '铁矿石',
    'KQ.m@DCE.j': '焦炭',
    'KQ.m@DCE.y': '豆油',
    'KQ.m@DCE.jd': '鸡蛋',
    'KQ.m@DCE.jm': '焦煤',
    'KQ.m@DCE.l': '塑料',
    'KQ.m@DCE.lh': '生猪',
    'KQ.m@DCE.m': '豆粕',
    'KQ.m@DCE.p': '棕榈油',
    'KQ.m@DCE.pg': '液化气',
    'KQ.m@DCE.pp': '聚丙烯',
    'KQ.m@DCE.v': '聚氯乙烯',
    'KQ.m@DCE.lg': '原木',

    # 广州期货交易所（GFEX）
    'KQ.m@GFEX.si': '工业硅',
    'KQ.m@GFEX.lc': '碳酸锂',
    'KQ.m@GFEX.ps': '多晶硅',

    # 中国金融期货交易所（CFFEX）
    'KQ.m@CFFEX.IC': '中证500',
    'KQ.m@CFFEX.IF': '沪深300',
    'KQ.m@CFFEX.IH': '上证50',
    'KQ.m@CFFEX.IM': '中证1000',
    'KQ.m@CFFEX.T': '10年国债',
    'KQ.m@CFFEX.TF': '5年国债',
    'KQ.m@CFFEX.TL': '30年国债'
}

# 引用tick数据
quotes = []
for symbol in SYMBOL_DICT.keys():
    quotes.append(api.get_quote(symbol))

# 引用K线数据
klines = []
for symbol in SYMBOL_DICT.keys():
    klines.append(api.get_kline_serial(symbol, PERIOD_LIST[period_index - 1]))

# 总数据字典
total_dict = {}

# 日志配置
logger = getLogger()
log_file = get_log_file_path(file_name)
set_log_rule(logger, log_file)

# 邮件配置
MAIL_AUTH_FILE = "./mail.json"
mail_addr = get_variable(MAIL_AUTH_FILE, "mail_addr", "")
auth_code = get_variable(MAIL_AUTH_FILE, "auth_code", "")
mail_to = get_variable(MAIL_AUTH_FILE, "mail_to", "")

# JSON文件
JSON_NAME = get_json_file_path(file_name)

log_and_print(f"【策略运行】策略: {CN_NAME} 周期: {PERIOD_LIST[period_index - 1]}秒")

while True:
    api.wait_update()

    # 与vb界面交互
    rw_path = Path("./rw_" + EN_NAME + "_" + str(period_index))
    #switch_path = Path("./switch_" + EN_NAME + "_" + str(period_index))
    sort_path = Path("./sort_" + EN_NAME + "_" + str(period_index))
    exit_path = Path("./exit_" + EN_NAME + "_" + str(period_index))
    if exists(exit_path):
        api.close()
        break

    # 获取当前时间
    now = datetime.now()
    #total_dict.clear()
    for index in range(len(SYMBOL_DICT)):
        # 白盘在夜间不显示
        if ((not quotes[index].trading_time.night) and (now.hour < 3 or now.hour > 19)):
            continue
        # 计算KD
        #kdj_k, _ = STOCH(klines[index].high, klines[index].low, klines[index].close, kdj_n, kdj_m1, 1, kdj_m2, 1)
        kdj = KDJ(klines[index], kdj_n, kdj_m1, kdj_m2)
        if (kdj["k"].iloc[-1] > kdj["d"].iloc[-1]):
            kd_sign = '++'
        elif (kdj["k"].iloc[-1] < kdj["d"].iloc[-1]):
            kd_sign = '--' 
        else:
            kd_sign = ''
        # 乖离率（采用EMA平滑计算）
        ma_ref = ema(klines[index].close, bias_n)
        bias = round((klines[index].close.iloc[-1] - ma_ref.iloc[-1]) / ma_ref.iloc[-1] * 100, 3)
        total_dict[list(SYMBOL_DICT.keys())[index]] = [quotes[index].last_price, bias, kd_sign, round(kdj["k"].iloc[-1], 3)]

    sleep(1.5)

    if exists(sort_path):
        filter_dict = {}
        for key in total_dict:
            if total_dict[key][1] > 0.0:
                filter_dict[key] = list(total_dict[key])
        sorted_total_dict = {k: v for k, v in sorted(filter_dict.items(), key=lambda x: x[1][3])}
    else:
        filter_dict = {}
        for key in total_dict:
            if total_dict[key][1] < 0.0:
                filter_dict[key] = list(total_dict[key])
        sorted_total_dict = {k: v for k, v in sorted(filter_dict.items(), key=lambda x: x[1][3], reverse=True)}

    ascend_dict_day = {}
    for key in total_dict[4]:
        if total_dict[4][key][1] > 0.0:
            ascend_dict_day[key] = list(total_dict[4][key])
    sorted_dict_day = {k: v for k, v in sorted(ascend_dict_day.items(), key=lambda x: x[1][3])}

    descend_dict_day = {}
    for key in total_dict[4]:
        if total_dict[4][key][1] < 0.0:
            descend_dict_day[key] = list(total_dict[4][key])
    sorted_dict_day = {k: v for k, v in sorted(descend_dict_day.items(), key=lambda x: x[1][3], reverse=True)}

    # 保存json文件
    if not exists(rw_path):
        clear_json_file(JSON_NAME)
        for symbol_cn in sorted_total_dict:
            save_variable(JSON_NAME, symbol_cn, sorted_total_dict[symbol_cn])
